/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.activiti.designer.eclipse.extension;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.designer.eclipse.common.ActivitiBPMNDiagramConstants;
import org.activiti.designer.eclipse.extension.export.ExportMarshaller;
import org.activiti.designer.eclipse.extension.validation.ProcessValidator;
import org.eclipse.bpmn2.FlowElement;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.graphiti.mm.pictograms.Diagram;
/**
* Abstract base class for diagram workers, such as {@link ExportMarshaller}s
* and {@link ProcessValidator}s.
*
* @author Tiese Barrell
* @since 0.6.1
* @version 2
*/
public abstract class AbstractDiagramWorker {
public static final String ATTRIBUTE_NODE_ID = "nodeId";
public static final String ATTRIBUTE_WORKER_ID = "workerId";
private static final String DATE_TIME_PATTERN = "yyyy-MM-dd-HH-mm-ss";
private static final SimpleDateFormat SDF = new SimpleDateFormat(DATE_TIME_PATTERN);
private static final String REGEX_DATE_TIME = "\\" + ExportMarshaller.PLACEHOLDER_DATE_TIME + "";
private static final String REGEX_FILENAME = "\\" + ExportMarshaller.PLACEHOLDER_ORIGINAL_FILENAME + "";
private static final String REGEX_FILENAME_WITHOUT_EXTENSION = "\\" + ExportMarshaller.PLACEHOLDER_ORIGINAL_FILENAME_WITHOUT_EXTENSION + "";
private static final String REGEX_EXTENSION = "\\" + ExportMarshaller.PLACEHOLDER_ORIGINAL_FILE_EXTENSION + "";
private static final int EXTRACTION_WORK_UNIT = 1;
/**
* Gets an {@link InputStream} to the contents of the provided {@link Diagram}
* .
*
* @param diagram
* the diagram to get the input stream for
* @return an input stream to the diagram's contents
*/
protected InputStream getInputStreamForDiagram(final Diagram diagram) {
InputStream result = null;
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IFile file = workspace.getRoot().getFile(new Path(getResourceForDiagram(diagram).getURI().toPlatformString(true)));
try {
result = file.getContents();
} catch (CoreException e) {
}
return result;
}
/**
* Gets the {@link Resource} associated with the provided {@link Diagram}.
*
* @param diagram
* the diagram to find the resource for
* @return the resource associated with the diagram
*/
protected Resource getResourceForDiagram(final Diagram diagram) {
return diagram.eResource();
}
/**
* Gets the {@link URI} associated with the provided {@link Diagram}'s
* resource.
*
* @param diagram
* the diagram to find the URI for
* @return the URI for the resource associated with the diagram
*/
protected URI getURIForDiagram(final Diagram diagram) {
return getResourceForDiagram(diagram).getURI();
}
/**
* Gets a new URI based on the provided {@link Diagram}s location and relative
* to the resource associated to the {@link Diagram}.
*
* <p>
* If replacement of {@link ExportMarshaller}'s replacement variables is
* required, the provided relativePath should contain these variables in the
* final segment of the path. Replacement variables in other segments will not
* be parsed and will result in exceptions.
*
* <p>
* <strong> Example usage: </strong>
* <p>
* if you wish to get a URI for a file named "my-file.xml" to be saved in the
* same directory as the original diagram, you would use:<br>
* {@link #getRelativeURIForDiagram(diagram, "my-file.xml")}.
* <p>
* To store the same file in a subdirectory called "my-dir" of the original
* diagram's directory, you would use<br>
* {@link #getRelativeURIForDiagram(diagram, "my-dir/my-file.xml")}.
* <p>
* To store the same file in the same subdirectory and use the original
* diagram's extension as the extension for the new resource you would use<br>
* {@link #getRelativeURIForDiagram(diagram, "my-dir/my-file." +
* ExportMarshaller.PLACEHOLDER_ORIGINAL_FILE_EXTENSION)}.
*
* @param diagram
* the diagram to find the URI for
* @param relativePath
* the relative path to the diagram provided
* @return the URI for the resource associated with the diagram
*/
protected URI getRelativeURIForDiagram(final Diagram diagram, final String relativePath) {
final URI originalURI = getResourceForDiagram(diagram).getURI();
String finalSegment = relativePath;
final URI parentURI = originalURI.trimSegments(1);
// Parse any replacement variables in the filename
final Calendar now = Calendar.getInstance();
finalSegment = finalSegment.replaceAll(REGEX_DATE_TIME, SDF.format(now.getTime()));
finalSegment = finalSegment.replaceAll(REGEX_EXTENSION, originalURI.fileExtension());
finalSegment = finalSegment.replaceAll(REGEX_FILENAME, originalURI.lastSegment());
finalSegment = finalSegment.replaceAll(REGEX_FILENAME_WITHOUT_EXTENSION, originalURI.trimFileExtension().lastSegment());
return parentURI.appendSegment(finalSegment);
}
/**
* Checks whether the provided resourceURI points to a resource that exists in
* the workspace.
*
* @param resourceURI
* @return
*/
protected boolean resourceExists(URI resourceURI) {
final IResource fileResource = ResourcesPlugin.getWorkspace().getRoot().findMember(resourceURI.toPlatformString(true));
return fileResource != null && fileResource.exists();
}
/**
* Gets a resource attached to the provided URI.
*
* @param resourceURI
* the URI to the resource
* @return a resource or null if there is none
*/
protected IResource getResource(URI resourceURI) {
IResource result = null;
if (resourceExists(resourceURI)) {
result = ResourcesPlugin.getWorkspace().getRoot().findMember(resourceURI.toPlatformString(true));
}
return result;
}
/**
* Saves a resource at the provided URI. Use this method to create or update
* resources created by {@link ExportMarshaller}s. This method adheres to the
* overwrite flag provided.
*
* <p>
* The URI provided is <strong>not</strong> parsed for replacements and is
* considered final when invoking this method. When obtaining the URI for a
* resource, replacements will be parsed if
* {@link #getRelativeURIForDiagram(Diagram, String)} is used.
*
* <p>
* To obtain a URI for the new resource you wish to create, invoke
* {@link #getRelativeURIForDiagram(Diagram, String)}.
*
* @see #getRelativeURIForDiagram(Diagram, String)
*
* @param uri
* the URI the resource should be saved to
* @param content
* a stream to the content for the resource
* @param overwriteFlag
* the flag for overwrite behavior
* @param monitor
* the progress monitor to use
*/
protected void saveResource(final URI uri, final InputStream content, final IProgressMonitor monitor) {
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IFile file = workspace.getRoot().getFile(new Path(uri.toPlatformString(true)));
// TODO
try {
if (file.exists()) {
// delete first
monitor.beginTask("update content", 10);
file.setContents(content, true, true, monitor);
} else {
monitor.beginTask("create", 10);
file.create(content, true, monitor);
}
file.refreshLocal(IResource.DEPTH_INFINITE, null);
monitor.worked(10);
} catch (CoreException e) {
// TODO
e.printStackTrace();
// addProblemToDiagram(diagram,
// "A problem occured while exectuing the export marshaller: " +
// e.getMessage(), null);
}
}
/**
* Extracts the process constructs from the provided list of {@link EObject}s
* in the diagram and places them in a Map.
*
* @param objects
* the list of {@link EObject}s in the diagram
* @param monitor
* the monitor to use to report progress
*
* @return a Map of process constructs, where the key is the type of the node
* and the value is a list of all constructs of that type found in the
* diagram.
*/
protected Map<String, List<EObject>> extractProcessConstructs(final List<EObject> objects, final IProgressMonitor monitor) {
monitor.beginTask("Analyzing process constructs", objects.size() * EXTRACTION_WORK_UNIT);
final Map<String, List<EObject>> result = new HashMap<String, List<EObject>>();
for (final EObject object : objects) {
if (object instanceof FlowElement) {
String nodeType = null;
// TODO: custom service tasks
// if (ExtensionUtil.isCustomServiceTask(object)) {
//
// final ServiceTask serviceTask = (ServiceTask) object;
// final CustomProperty customProperty =
// ExtensionUtil.getCustomProperty(serviceTask,
// ExtensionConstants.PROPERTY_ID_CUSTOM_SERVICE_TASK);
//
// if (customProperty != null) {
// final String nodeType = customProperty.getSimpleValue();
//
//
// }
// }
nodeType = object.getClass().getCanonicalName();
if (nodeType != null) {
if (!result.containsKey(nodeType)) {
result.put(nodeType, new ArrayList<EObject>());
}
result.get(nodeType).add(object);
}
}
monitor.worked(EXTRACTION_WORK_UNIT);
}
monitor.done();
return result;
}
/**
* Adds a marker to the {@link Diagram} provided that has an
* {@link IMarker#SEVERITY_INFO} severity (info).
*/
protected void addInfoToDiagram(Diagram diagram, String message, String nodeId) {
addMarkerToDiagram(diagram, message, nodeId, IMarker.SEVERITY_INFO);
}
/**
* Adds a marker to the {@link Diagram} provided that has an
* {@link IMarker#SEVERITY_WARNING} severity (warning).
*/
protected void addWarningToDiagram(Diagram diagram, String message, String nodeId) {
addMarkerToDiagram(diagram, message, nodeId, IMarker.SEVERITY_WARNING);
}
/**
* Adds a marker to the {@link Diagram} provided that has an
* {@link IMarker#SEVERITY_ERROR} severity (error).
*/
protected void addProblemToDiagram(Diagram diagram, String message, String nodeId) {
addMarkerToDiagram(diagram, message, nodeId, IMarker.SEVERITY_ERROR);
}
private void addMarkerToDiagram(Diagram diagram, String message, String nodeId, final int severity) {
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IFile file = workspace.getRoot().getFile(new Path(getURIForDiagram(diagram).toPlatformString(true)));
// Determine marker id
String markerId = ActivitiBPMNDiagramConstants.ACTIVITI_GENERAL_MARKER_ID;
if (this instanceof ExportMarshaller) {
markerId = ExportMarshaller.MARKER_ID;
} else if (this instanceof ProcessValidator) {
markerId = ProcessValidator.MARKER_ID;
}
IMarker m;
try {
m = file.createMarker(markerId);
if (nodeId != null) {
m.setAttribute(ATTRIBUTE_NODE_ID, nodeId);
}
m.setAttribute(ATTRIBUTE_WORKER_ID, this.getClass().getCanonicalName());
m.setAttribute(IMarker.MESSAGE, message);
m.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
m.setAttribute(IMarker.SEVERITY, severity);
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void clearMarkers(IResource resource) {
// Determine marker id
String markerId = ActivitiBPMNDiagramConstants.ACTIVITI_GENERAL_MARKER_ID;
if (this instanceof ExportMarshaller) {
markerId = ExportMarshaller.MARKER_ID;
} else if (this instanceof ProcessValidator) {
markerId = ProcessValidator.MARKER_ID;
}
try {
final IMarker[] markers = resource.findMarkers(markerId, true, IResource.DEPTH_INFINITE);
for (final IMarker marker : markers) {
if (marker.getAttribute(ATTRIBUTE_WORKER_ID).equals(this.getClass().getCanonicalName())) {
marker.delete();
}
}
} catch (CoreException e) {
e.printStackTrace();
}
}
protected IMarker[] getMarkers(IResource resource) {
IMarker[] markers = null;
try {
markers = resource.findMarkers(ActivitiBPMNDiagramConstants.ACTIVITI_GENERAL_MARKER_ID, true, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
e.printStackTrace();
}
return markers;
}
}